package cn.jh.common.utils;

import com.google.common.base.Preconditions;
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.util.Calendar;
import java.util.Date;

import static com.google.common.base.Preconditions.checkArgument;


public class DateUtil {

    @NotNull
	public static Date fromLocal(@NotNull LocalDateTime ldt) {
        Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }

    @NotNull
    public static LocalDateTime toDateTime(Date date) {
        Instant instant = date.toInstant();
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    @NotNull
    public static Range<LocalDate> lastWeekDateRange() {
        return lastWeekDateRange(LocalDate.now());
    }

    @NotNull
    @Contract("null -> fail")
    public static Range<LocalDate> lastWeekDateRange(LocalDate date) {
        checkArgument(date != null, "The date must be not null");
        LocalDate lastWeekThisDay = date.minusWeeks(1);
        DayOfWeek dayOfWeek = lastWeekThisDay.getDayOfWeek();
        int minusDays = dayOfWeek.ordinal(),
                plusDays = 7 - dayOfWeek.getValue();
        LocalDate from = minusDays == 0 ? lastWeekThisDay : lastWeekThisDay.minusDays(minusDays);
        LocalDate to = plusDays == 0? lastWeekThisDay : lastWeekThisDay.plusDays(plusDays);
        return Range.closed(from, to);
    }

    @NotNull
    public static Range<LocalDateTime> lastWeekDateTimeRange() {
        return lastWeekDateTimeRange(LocalDate.now());
    }

    @NotNull
    @Contract("null -> fail")
    public static Range<LocalDateTime> lastWeekDateTimeRange(LocalDate date) {
        Range<LocalDate> dateRange = lastWeekDateRange(date);
        LocalDateTime from = dateRange.lowerEndpoint().atStartOfDay();
        LocalDateTime to = dateRange.upperEndpoint().atTime(LocalTime.MAX);
        return Range.closed(from, to);
    }

    @Contract("null -> fail")
    public static Range<Date> toOldDateRange(Range<LocalDateTime> range) {
        checkArgument(range != null, "The given range must be not null");
        Date from = null, to = null;
        BoundType lowerBoundType = range.lowerBoundType(),
                upperBoundType = range.upperBoundType();
        if (range.hasLowerBound()) {
            from = fromLocal(range.lowerEndpoint());
        }
        if (range.hasUpperBound()) {
            to = fromLocal(range.upperEndpoint());
        }
        if (from != null) {
            if (to == null) {
                return Range.downTo(from, lowerBoundType);
            }
            return Range.range(from, lowerBoundType, to, upperBoundType);
        } else {
            if (to == null) {
                return Range.all();
            }
            return Range.upTo(to, upperBoundType);
        }
    }

    @Contract("null -> null")
	public static Date getDateFromStr(String str) {
        if (str == null) return null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		try {
			return sdf.parse(str);
		} catch (ParseException e) {
			return null;
		}
	}

    @Contract("null -> null")
	public static Date getYYMMHHMMSSDateFromStr(String str) {
        if (str == null) return null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		try {
			return sdf.parse(str);
		} catch (ParseException ignored) {
			return null;
		}

	}

    @Contract("null -> null")
	public static Date getHHMMSSDateFormat(String str) {
        if (str == null) return null;
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		try {
			return sdf.parse(str);
		} catch (Exception ignored) {
			return null;
		}
	}

	@Contract("null -> null")
	public static String getyyyyMMddHHmmssDateFormat(Date date) {
	    if (date == null) return null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		try {
			return sdf.format(date);
		} catch (Exception ignored) {
			return null;
		}

	}

	@Contract("null -> null")
	public static String getyyyyMMddHHmmssSSSDateFormat(Date date) {
	    if (date == null) return null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		try {
			return sdf.format(date);
		} catch (Exception ignored) {
			return null;
		}
	}

	public static String getDateFromStr(Date date) {

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		String result = null;
		try {
			result = sdf.format(date);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	public static <T, U> T  getDateStringConvert(T target, U source,String parrten){
		SimpleDateFormat sdf = new SimpleDateFormat(parrten);
		if(target instanceof Date){
			if(source instanceof String){
				try {
					return (T) sdf.parse((String)source);
				} catch (ParseException e) {
					throw new RuntimeException(e);
				}
			}else if(source instanceof Date){
				try {
					return (T) sdf.parse(sdf.format(source));
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		}else{
			if(source instanceof Date){
				try {
					return (T) sdf.format((Date)source);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}else if(source instanceof String){
				try {
					return (T) sdf.parse((sdf.format(source)));
				} catch (ParseException e) {
					throw new RuntimeException(e);
				}
			}
		}
		return null;
	}
	
}
